package cn.spdb.harrier.server.worker.task;

import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.io.Resources;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import cn.spdb.harrier.common.utils.Symbol;
import cn.spdb.harrier.server.entity.JobStepBean;
import cn.spdb.harrier.server.worker.exec.UdsSqlScriptRunner;

public class SqlTask extends AbstractTask {

	private Properties properties = new Properties();

	private static String SQL_DELIMITER = "sql.delimiter";
	private static String SQL_SEND_FULL_SCRIPT = "sql.send.full.script";

	private Boolean sendFull;
	private String delimiter;

	private Connection con;

	public SqlTask(JobStepBean jobStepBean) {
		super(jobStepBean);
	}

/**
 * 初始化 SQL 任务的运行时配置参数。
 *
 * 该方法从 jobStepBean 的环境变量中读取 SQL 执行相关的配置：
 * - 设置 SQL 语句的分隔符，默认为分号（`;`）；
 * - 设置是否发送完整脚本执行的标志，默认为 true。
 *
 * 同时设置初始退出状态码为 902，表示任务尚未完成或出现异常。
 */
@Override
public void init() {
    // 设置默认退出状态码为 902，表示初始化完成但任务未成功结束
    setExitStatusCode(902);

    // 清空之前的属性配置，确保每次任务独立
    properties.clear();

    // 记录任务开始日志
    logger.info("SqlTask start");

    // 将作业步骤的环境变量加载到 properties 中
    properties.putAll(jobStepBean.getEnvMap());

    // 获取 SQL 分隔符配置，若未配置则使用默认值分号 ";"
    delimiter = properties.getProperty(SQL_DELIMITER);
    if (StringUtils.isEmpty(delimiter)) {
        delimiter = Symbol.FEN_HAO; // 默认分隔符：";"
    }

    // 获取是否发送完整脚本的配置，若未配置则默认为 true
    String sendFullStr = properties.getProperty(SQL_SEND_FULL_SCRIPT);
    if (StringUtils.isEmpty(sendFullStr)) {
        sendFull = true;
    } else {
        sendFull = Boolean.valueOf(sendFullStr);
    }
}


/**
 * 执行 SQL 任务的核心逻辑。
 *
 * 该方法主要完成以下工作：
 * 1. 根据配置创建数据源（DataSource）；
 * 2. 获取数据库连接并设置事务控制；
 * 3. 构建 SQL 脚本执行器（UdsSqlScriptRunner），并配置其行为；
 * 4. 判断 SQL 内容来源：
 *    - 若 stepPath 为空，则从 jobStepBean 的命令和参数拼接 SQL；
 *    - 否则读取指定路径的 SQL 文件；
 * 5. 执行 SQL 脚本，并根据执行结果更新退出状态码。
 */
@Override
public void handle() {
    DataSource datasource = null;
    try {
        // 创建基于 Druid 的数据源
        datasource = DruidDataSourceFactory.createDataSource(properties);
    } catch (Exception e1) {
        e1.printStackTrace();
        logger.error("数据源创建失败", e1);
    }

    if (datasource == null) {
        logger.error("数据源为空，无法继续执行");
        return;
    }

    // 使用 try-with-resources 确保连接自动关闭
    try (Connection connection = this.con = datasource.getConnection()) {
        connection.setAutoCommit(false); // 开启手动提交事务

        // 创建 SQL 脚本执行器
        UdsSqlScriptRunner scriptRunner = new UdsSqlScriptRunner(connection, getLogger());
        scriptRunner.setAutoCommit(false);         // 不自动提交
        scriptRunner.setStopOnError(true);          // 出错停止
        scriptRunner.setSendFullScript(sendFull);   // 设置是否发送完整脚本
        scriptRunner.setDelimiter(delimiter);       // 设置语句分隔符
        scriptRunner.setFullLineDelimiter(false);   // 分隔符不需整行匹配

        Resources.setCharset(StandardCharsets.UTF_8); // 设置资源加载字符集

        logger.info("SQL 脚本开始执行");

        // 根据是否有文件路径决定 SQL 来源
        if (StringUtils.isEmpty(jobStepBean.getStepPath())) {
            // 从命令和参数拼接 SQL 字符串
            String[] strArray = jobStepBean.getCmd().split(Symbol.WEN_HAO);
            List<String> list = jobStepBean.getParameterList();
            StringBuffer sbuffer = new StringBuffer();
            for (int i = 0; i < strArray.length; i++) {
                sbuffer.append(strArray[i]);
                String para = list.get(i);
                if (StringUtils.isNotEmpty(para)) {
                    sbuffer.append(Symbol.DAN_YING_HAO).append(para).append(Symbol.DAN_YING_HAO);
                }
            }
            try (StringReader reader = new StringReader(sbuffer.toString());) {
                scriptRunner.runScript(reader); // 执行拼接后的 SQL
            }
        } else {
            // 从指定文件路径读取 SQL 文件内容
            File file = new File(jobStepBean.getStepPath());
            if (file.exists()) {
                try (Reader reader = new FileReader(file);) {
                    scriptRunner.runScript(reader); // 执行文件中的 SQL
                }
            }
        }

        logger.info("SQL 脚本执行结束");
        setExitStatusCode(0); // 设置成功退出状态码
    } catch (Exception e) {
        logger.error("SQL 执行过程中发生错误", e);
    }
}

	@Override
	protected void subkill() {
		if (ObjectUtils.isNotEmpty(this.con)) {
			try {
				this.con.close();
			} catch (SQLException e) {
				e.printStackTrace();
				logger.error("kill effect", e);
			}
		}
	}

}
